<!DOCTYPE html>
<html lang=zh-CN>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="google-site-verification" content="xBT4GhYoi5qRD5tr338pgPM5OWHHIDR6mNg1a3euekI" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta property="og:description" content="samuel-24276的博客">
    <meta property="og:type" content="website">
    <meta name="description" content="samuel-24276的博客">
    <meta name="keyword"  content="aircloud">
    <link rel="shortcut icon" href="/myblog/img/favicon.ico">

    <title>
        
        回调函数 - samuel-24276的博客 | samuel-24276&#39;s Blog
        
    </title>

    <!-- Custom CSS -->
    
<link rel="stylesheet" href="/myblog/css/aircloud.css">

    
<link rel="stylesheet" href="/myblog/css/gitment.css">

    <!--<link rel="stylesheet" href="https://imsun.github.io/gitment/style/default.css">-->
    <link href="//at.alicdn.com/t/font_620856_28hi1hpxx24.css" rel="stylesheet" type="text/css">
    <!-- ga & ba script hoook -->
    <script></script>
<meta name="generator" content="Hexo 7.1.1"></head>

<body>

<div class="site-nav-toggle" id="site-nav-toggle">
    <button>
        <span class="btn-bar"></span>
        <span class="btn-bar"></span>
        <span class="btn-bar"></span>
    </button>
</div>

<div class="index-about">
    <i> Like what you do, do what you like </i>
</div>

<div class="index-container">
    
    <div class="index-left">
        
<div class="nav" id="nav">
    <div class="avatar-name">
        <div class="avatar ">
            <img src="/myblog/img/avatar.jpg" />
        </div>
        <div class="name">
            <i>samuel-24276</i>
        </div>
    </div>
    <div class="contents" id="nav-content">
        <ul>
            <li >
                <a href="/myblog/">
                    <i class="iconfont icon-shouye1"></i>
                    <span>主页</span>
                </a>
            </li>
            <li >
                <a href="/myblog/tags">
                    <i class="iconfont icon-biaoqian1"></i>
                    <span>标签</span>
                </a>
            </li>
            <li >
                <a href="/myblog/archives">
                    <i class="iconfont icon-guidang2"></i>
                    <span>存档</span>
                </a>
            </li>
            <li >
                <a href="/myblog/collect/">
                    <i class="iconfont icon-shoucang1"></i>
                    <span>收藏</span>
                </a>
            </li>
            <li >
                <a href="/myblog/about/">
                    <i class="iconfont icon-guanyu2"></i>
                    <span>关于</span>
                </a>
            </li>
            
            <li>
                <a id="search">
                    <i class="iconfont icon-sousuo1"></i>
                    <span>搜索</span>
                </a>
            </li>
            
        </ul>
    </div>
    
        <div id="toc" class="toc-article">
    <ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#1-%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0"><span class="toc-text">1.回调函数</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#2-%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0%E3%80%81%E6%B6%88%E6%81%AF%E5%92%8C%E4%BA%8B%E4%BB%B6%E4%BE%8B%E7%A8%8B"><span class="toc-text">2.回调函数、消息和事件例程</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#4-%E8%B0%83%E7%94%A8%E6%96%B9%E5%BC%8F"><span class="toc-text">4.调用方式</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#4-1-%E8%BF%87%E7%A8%8B%E8%AF%AD%E8%A8%80%E4%B8%AD%E7%9A%84%E5%9B%9E%E8%B0%83%EF%BC%88C%EF%BC%89"><span class="toc-text">4.1.过程语言中的回调（C）</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#4-1-1-%E5%87%BD%E6%95%B0%E6%8C%87%E9%92%88"><span class="toc-text">4.1.1.函数指针</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-1-2-%E5%8F%82%E6%95%B0%E4%BC%A0%E9%80%92%E8%A7%84%E5%88%99"><span class="toc-text">4.1.2.参数传递规则</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-1-3-%E5%BA%94%E7%94%A8%E4%B8%BE%E4%BE%8B"><span class="toc-text">4.1.3.应用举例</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#5-Qt%E8%B0%83%E7%94%A8%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0"><span class="toc-text">5.Qt调用回调函数</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#5-1-dll%E5%9B%9E%E8%B0%83%E4%B8%BB%E7%A8%8B%E5%BA%8F%E6%96%B9%E6%B3%95"><span class="toc-text">5.1.dll回调主程序方法</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#5-2-%E4%B8%BB%E7%A8%8B%E5%BA%8F%E4%BC%A0%E5%85%A5%E8%A2%AB%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0%E5%9C%B0%E5%9D%80"><span class="toc-text">5.2.主程序传入被回调函数地址</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#5-3-%E5%9C%A8%E5%90%88%E9%80%82%E6%97%B6%E6%9C%BA%E5%88%9D%E5%A7%8B%E5%8C%96%E5%92%8C%E9%94%80%E6%AF%81"><span class="toc-text">5.3.在合适时机初始化和销毁</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#6-%E7%B1%BB%E6%88%90%E5%91%98%E5%87%BD%E6%95%B0%E4%BD%9C%E4%B8%BA%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0%E7%9A%84%E6%96%B9%E6%B3%95%E5%8F%8A%E6%B3%A8%E6%84%8F%E7%82%B9"><span class="toc-text">6.类成员函数作为回调函数的方法及注意点</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#6-1-%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0%E4%B8%BA%E6%99%AE%E9%80%9A%E7%9A%84%E5%85%A8%E5%B1%80%E5%87%BD%E6%95%B0%EF%BC%8C%E4%BD%86%E5%9C%A8%E5%87%BD%E6%95%B0%E4%BD%93%E5%86%85%E6%89%A7%E8%A1%8C%E7%B1%BB%E7%9A%84%E6%88%90%E5%91%98%E5%87%BD%E6%95%B0"><span class="toc-text">6.1.回调函数为普通的全局函数，但在函数体内执行类的成员函数</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#6-2-%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0%E4%B8%BA%E7%B1%BB%E5%86%85%E9%9D%99%E6%80%81%E6%88%90%E5%91%98%E5%87%BD%E6%95%B0%EF%BC%8C%E5%9C%A8%E5%85%B6%E5%86%85%E9%83%A8%E8%B0%83%E7%94%A8%E7%B1%BB%E7%9A%84%E9%9D%9E%E9%9D%99%E6%80%81%E6%88%90%E5%91%98%E5%87%BD%E6%95%B0"><span class="toc-text">6.2.回调函数为类内静态成员函数，在其内部调用类的非静态成员函数</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#6-3-%E5%AF%B9%E6%88%90%E5%91%98%E5%87%BD%E6%95%B0%E8%BF%9B%E8%A1%8C%E5%BC%BA%E5%88%B6%E8%BD%AC%E6%8D%A2%EF%BC%8C%E4%BD%BF%E5%85%B6%E4%BD%9C%E4%B8%BA%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0-%E5%BB%BA%E8%AE%AE"><span class="toc-text">6.3.对成员函数进行强制转换，使其作为回调函数(建议)</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#6-4-%E4%B8%BA%E4%BB%80%E4%B9%88%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0%E5%BF%85%E9%A1%BB%E4%B8%BA%E9%9D%99%E6%80%81%E5%87%BD%E6%95%B0%EF%BC%9F"><span class="toc-text">6.4.为什么回调函数必须为静态函数？</span></a></li></ol></li></ol>
</div>
    
</div>


<div class="search-field" id="search-field">
    <div class="search-bg" id="search-bg"></div>
    <div class="search-container">
        <div class="search-input">
            <span id="esc-search"> <i class="icon-fanhui iconfont"></i></span>
            <input id="search-input"/>
            <span id="begin-search">搜索</span>
        </div>
        <div class="search-result-container" id="search-result-container">

        </div>
    </div>
</div>

        <div class="index-about-mobile">
            <i> Like what you do, do what you like </i>
        </div>
    </div>
    
    <div class="index-middle">
        <!-- Main Content -->
        


<div class="post-container">
    <div class="post-title">
        回调函数
    </div>

    <div class="post-meta">
        <span class="attr">发布于：<span>2021-12-09 21:25:26</span></span>
        
        <span class="attr">标签：/
        
        <a class="tag" href="/myblog/tags/#C++" title="C++">C++</a>
        <span>/</span>
        
        
        </span>
        <span class="attr">访问：<span id="busuanzi_value_page_pv"></span>
</span>
</span>
    </div>
    <div class="post-content no-indent">
        <blockquote>
<p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/hualalasummer/p/3712179.html">C++回调函数用法</a></p>
</blockquote>
<h1 id="1-回调函数"><a href="#1-回调函数" class="headerlink" title="1.回调函数"></a>1.回调函数</h1><p>　　我们经常在C++设计时通过使用回调函数可以使有些应用（如定时器事件回调处理、用回调函数记录某操作进度等）变得非常方便和符合逻辑，那么它的内在机制如何呢，怎么定义呢?它和其它函数（比如钩子函数）有何不同呢？</p>
<p>　　<strong>使用回调函数实际上就是在调用某个函数（通常是API函数）时，将自己的一个函数（这个函数为回调函数）的地址作为参数传递给那个函数</strong>。就像机器猫给大雄一个任意门，大雄决定什么时候开启任意门，任意门到达的地点是机器猫设定好的。</p>
<p>　 　<em>而那个函数在需要的时候，利用传递的地址调用回调函数，这时你可以利用这个机会在回调函数中处理消息或完成一定的操作</em>。至于如何定义回调函数，跟具体使用的 API函数有关，一般在帮助中有说明回调函数的参数和返回值等。C++中一般要求在回调函数前加CALLBACK（相当于FAR PASCAL），这主要是说明该函数的调用方式。</p>
<p>　　至于钩子函数，只是回调函数的一个特例。习惯上把与SetWindowsHookEx函数一起使用的回调函数称为钩子函数。也有人把利用VirtualQueryEx安装的函数称为钩子函数，不过这种叫法不太流行。</p>
<p>　　也可以这样，更容易理解：回调函数就好像是一个中断处理函数，系统在符合你设定的条件时自动调用。为此，你需要做三件事：</p>
<ul>
<li>1.声明；</li>
<li>2.定义；</li>
<li>3.设置触发条件，就是在你的函数中把你的回调函数名称转化为地址作为一个参数，以便于系统调用。</li>
</ul>
<p>　　声明和定义时应注意：<strong>回调函数由系统调用</strong>，所以可以认为它<strong>属于WINDOWS系统，不要把它当作你的某个类的成员函数</strong>。</p>
<h1 id="2-回调函数、消息和事件例程"><a href="#2-回调函数、消息和事件例程" class="headerlink" title="2.回调函数、消息和事件例程"></a>2.回调函数、消息和事件例程</h1><p>　 　<em>调用(calling)机制从汇编时代起已经大量使用：准备一段现成的代码，调用者可以随时跳转至此段代码的起始地址，执行完后再返回跳转时的后续地址</em>。 CPU为此准备了现成的调用指令，调用时可以压栈保护现场，调用结束后从堆栈中弹出现场地址，以便自动返回。借堆栈保护现场真是一项绝妙的发明，它使调用 者和被调者可以互不相识，于是才有了后来的函数和构件。</p>
<p>　　此调用机制并非完美。回调函数就是一例。函数之类本是为调用者准备的美餐， 其烹制者应对食客了如指掌，但实情并非如此。例如，写一个快速排序函数供他人调用，其中必包含比较大小。麻烦来了：此时并不知要比较的是何类数据–整数、浮点数、字符串？于是只好为每类数据制作一个不同的排序函数。更通行的办法是 在函数参数中列一个回调函数地址，并通知调用者：君需自己准备一个比较函数，其中包含两个指针类参数，函数要比较此二指针所指数据之大小，并由函数返回值说明比较结果。排序函数借此调用者提供的函数来比较大小，借指针传递参数，可以全然不管所比较的数据类型。<strong>被调用者回头调用调用者的函数（够咬嘴的），故称其为回调（callback）</strong>。</p>
<p>　　回调函数使程序结构乱了许多。Windows API 函数集中有不少回调函数，尽管有详尽说明，仍使初学者一头雾水。恐怕这也是无奈之举。</p>
<p>　　无论何种事物，能以树形结构单向描述毕竟让人舒服些。如果某家族中孙辈又是某祖辈的祖辈，恐怕无人能理清其中的头绪。但数据处理之复杂往往需要构成网状结构，非简单的客户&#x2F;服务器关系能穷尽。</p>
<p>　 　<strong>Windows系统还包含着另一种更为广泛的回调机制，即消息机制</strong>。消息本是 Windows 的基本控制手段，乍看与函数调用无关，其实是一种变相的函数调用。<strong>发送消息的目的是通知收方运行一段预先准备好的代码，相当于调用一个函数。消息所附带的 WParam 和 LParam 相当于函数的参数，只不过比普通参数更通用一些</strong>。应用程序可以主动发送消息，更多情况下是坐等 Windows 发送消息。一旦消息进入所属消息队列，便检感兴趣的那些，跳转去执行相应的消息处理代码。操作系统本是为应用程序服务，由应用程序来调用。而应用程序一旦 启动，却要反过来等待操作系统的调用。这分明也是一种回调，或者说是一种<em><strong>广义回调</strong></em>。其实，应用程序之间也可以形成这种回调。假如进程 B 收到进程 A 发来的消息，启动了一段代码，其中又向进程 A 发送消息，这就形成了回调。这种回调比较隐蔽，弄不好会搞成递归调用，若缺少终止条件，将会循环不已，直至把程序搞垮。若是故意编写成此递归调用，并设好 终止条件，倒是很有意思。但这种程序结构太隐蔽，除非十分必要，还是不用为好。</p>
<p>利用消息也可以构成<em><strong>狭义回调</strong></em>。上面所举排序函数一例，可以把回调函数地址换成窗口 handle。如此，当需要比较数据大小时，不是去调用回调函数，而是借 API 函数 SendMessage 向指定窗口发送消息。收到消息方负责比较数据大小，把比较结果通过消息本身的返回值传给消息发送方。所实现的功能与回调函数并无不同。当然，此例中改为消息纯属画蛇添脚，反倒把程序搞得很慢。但其他情况下并非总是如此，特别是<strong>需要异步调用时，发送消息是一种不错的选择</strong>。<em>假如回调函数中包含文件处理之类的低速处理，调用方等不得，需要把同步调用改为异步调用，去启动一个单独的线程，然后马上执行后续代码，其余的事让线程慢慢去做</em>。<strong>一个替代办法是借 API 函数 PostMessage 发送一个异步消息，然后立即执行后续代码</strong>。这要比自己搞个线程省事许多，而且更安全。</p>
<p>　　如今我们是活在一个 object 时代。只要与编程有关，无论何事都离不开 object。但 object 并未消除回调，反而把它发扬光大，弄得到处都是，只不过大都以事件（event）的身份出现，镶嵌在某个结构之中，显得更正统，更容易被人接受。应用程序要使用某个构件，总要先弄清构件的属性、方法和事件，然后给构件属性赋值，在适当的时候调用适当的构件方法，还要给事件编写处理例程，以备构件代码来调用。<strong>何谓事件？它不过是一个指向事件进程的地址，与回调函数地址没什么区别</strong>。</p>
<p>　　不过，此种回调方式比传统回调函数要高明许多。首先， 它把让人不太舒服的回调函数变成一种自然而然的处理例程，使编程者顿觉气顺。再者，地址是一个危险的东西，用好了可使程序加速，用不好处处是陷阱，程序随时都会崩溃。现代编程方式总是想法把地址隐藏起来（隐藏比较彻底的如 VB 和 Java），其代价是降低了程序效率。<strong>事件进程</strong>使编程者无需直接操作地址，但并不会使程序减速。</p>
<p>　　（例程似乎是进程的台湾翻译。）</p>
<p>　# 3.精妙比喻:</p>
<pre><code>    回调函数还真有点像您随身带的BP机：告诉别人号码，在它有事情时Call您。
</code></pre>
<p>　 　<strong>回调用于层间协作，上层将本层函数安装在下层，这个函数就是回调，而下层在一定条件下触发回调</strong>，例如作为一个驱动，是一个底层，他在收到一个数据时，除了完成本层的处理工作外，还将进行回调，将这个数据交给上层应用层来做进一步处理，这在分层的数据通信中很普遍。其实<strong>回调和API非常接近，他们的共性都是跨层调用的函数</strong>。但<em>区别是API是低层提供给高层的调用，一般这个函数对高层都是已知的；而回调正好相反，他是高层提供给底层的调用，对于低层他是未知 的，必须由高层进行安装，这个安装函数其实就是一个低层提供的API，安装后低层不知道这个回调的名字，但它通过一个函数指针来保存这个回调，在需要调用 时，只需引用这个函数指针和相关的参数指针</em>。 其实：回调就是该函数写在高层，低层通过一个函数指针保存这个函数，在某个事件的触发下，低层通过该函数指针调用高层那个函数。</p>
<h1 id="4-调用方式"><a href="#4-调用方式" class="headerlink" title="4.调用方式"></a>4.调用方式</h1><p>　 　软件模块之间总是存在着一定的接口，从调用方式上，可以把他们分为三类：<strong>同步调用、回调和异步调用</strong>。同步调用是一种阻塞式调用，调用方要等待对方执行完毕才返回，它是一种<em>单向调用</em>；回调是一种<em>双向调用</em>模式，也就是说，被调用方在接口被调用时也会调用对方的接口；异步调用是一种类似消息或事件的机制，不过它的<em>调用方向刚好相反</em>，接口的服务在收到某种讯息或发生某种事件时，会主动通知客户方（即调用客户方的接口）。回调和异步调用的关系非常紧密，<strong>通常我们使用回调来实现异步消息的注册，通过异步调用来实现消息的通知。同步调用是三者当中最简单的，而回调又常常是异步调用的基础</strong>。</p>
<p>　　对于不同 类型的语言（如结构化语言和对象语言）、平台（Win32、JDK）或构架（CORBA、DCOM、WebService），客户和服务的交互除 了同步方式以外，都需要具备一定的异步通知机制，让服务方（或接口提供方）在某些情况下能够主动通知客户，而<strong>回调是实现异步的一个最简捷的途径</strong>。</p>
<p>　　对于一般的结构化语言，可以通过回调函数来实现回调。<strong>回调函数也是一个函数或过程，不过它是一个由调用方自己实现，供被调用方使用的特殊函数</strong>。</p>
<p>　 　<em>在面向对象的语言中，回调则是通过接口或抽象类来实现的，我们把实现这种接口的类成为回调类，回调类的对象成为回调对象。对于象<strong>C++或Object Pascal这些兼容了过程特性的对象语言</strong>，不仅提供了回调对象、回调方法等特性，也<strong>能兼容过程语言的回调函数机制</strong></em>。</p>
<p>　　Windows平台的消息机制也可以看作是回调的一种应用，我们通过系统提供的接口注册消息处理函数（即回调函数），从而实现接收、处理消息的目的。由于Windows平台的API是用C语言来构建的，我们可以认为它也是回调函数的一个特例。</p>
<p>　　对于分布式组件代理体系CORBA，异步处理有多种方式，如回调、事件服务、通知服务等。事件服务和通知服务是CORBA用来处理异步消息的标准服务，他们主要负责消息的处理、派发、维护等工作。对一些简单的异步处理过程，我们可以通过回调机制来实现。</p>
<p>　　下面我们集中比较具有代表性的语言（C、Object Pascal）和架构（CORBA）来分析回调的实现方式、具体作用等。</p>
<h2 id="4-1-过程语言中的回调（C）"><a href="#4-1-过程语言中的回调（C）" class="headerlink" title="4.1.过程语言中的回调（C）"></a>4.1.过程语言中的回调（C）</h2><h3 id="4-1-1-函数指针"><a href="#4-1-1-函数指针" class="headerlink" title="4.1.1.函数指针"></a>4.1.1.函数指针</h3><p>　　回调在C语言中是通过函数指针来实现的,通过将回调函数的地址传给被调函数从而实现回调。因此，要实现回调，必须首先定义函数指针，请看下面的例子：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">Func</span><span class="params">(<span class="type">char</span> *s)</span>；<span class="comment">// 函数原型</span></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"><span class="title">void</span> <span class="params">(*pFunc)</span> <span class="params">(<span class="type">char</span> *)</span></span>;<span class="comment">//函数指针</span></span><br></pre></td></tr></table></figure>

<p>　　可以看出，函数的定义和函数指针的定义非常类似。</p>
<p>　　一般的话，为了简化函数指针类型的变量定义，提高程序的可读性，我们需要把函数指针类型自定义一下。</p>
<figure class="highlight delphi"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">typedef void<span class="comment">(*pcb)(char *)</span>;</span><br></pre></td></tr></table></figure>

<p>　　被调函数的例子：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">GetCallBack</span><span class="params">(pcb callback)</span> </span></span><br><span class="line"><span class="function"></span>&#123; </span><br><span class="line"><span class="comment">/*do something*/</span> </span><br><span class="line">&#125; </span><br><span class="line"><span class="comment">//用户在调用上面的函数时，需要自己实现一个pcb类型的回调函数： </span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">fCallback</span><span class="params">(<span class="type">char</span> *s)</span> </span></span><br><span class="line"><span class="function"></span>&#123; </span><br><span class="line"><span class="comment">/* do something */</span> </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>　　然后，就可以直接把fCallback当作一个变量传递给GetCallBack,</p>
<figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">GetCallBack（fCallback）<span class="comment">;</span></span><br></pre></td></tr></table></figure>

<p>　　如果赋了不同的值给该参数，那么调用者将调用不同地址的函数。赋值可以发生在运行时，这样使你能实现动态绑定。</p>
<h3 id="4-1-2-参数传递规则"><a href="#4-1-2-参数传递规则" class="headerlink" title="4.1.2.参数传递规则"></a>4.1.2.参数传递规则</h3><p>　 　到目前为止，我们只讨论了函数指针及回调而没有去注意ANSI C&#x2F;C++的编译器规范。许多编译器有几种调用规范。如在Visual C++中，可以在函数类型前加_cdecl，_stdcall或者_pascal来表示其调用规范（默认为_cdecl）。C++ Builder也支持_fastcall调用规范。<strong>调用规范影响编译器产生的给定函数名，参数传递的顺序（从右到左或从左到右），堆栈清理责任（调用者或 者被调用者）以及参数传递机制（堆栈，CPU寄存器等）</strong>。</p>
<p>　　将调用规范看成是函数类型的一部分是很重要的；不能用不兼容的调用规范将地址赋值给函数指针。例如：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 被调用函数是以int为参数，以int为返回值</span></span><br><span class="line"><span class="function">__stdcall <span class="type">int</span> <span class="title">callee</span><span class="params">(<span class="type">int</span>)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 调用函数以函数指针为参数</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">caller</span><span class="params">( __cdecl <span class="type">int</span>(*ptr)(<span class="type">int</span>))</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 在p中企图存储被调用函数地址的非法操作</span></span><br><span class="line"><span class="function">__cdecl <span class="title">int</span><span class="params">(*p)</span><span class="params">(<span class="type">int</span>)</span> </span>= callee; <span class="comment">// 出错</span></span><br></pre></td></tr></table></figure>

<p>　　<strong>指针p和callee()的类型不兼容，因为它们有不同的调用规范(__stdcall和__cdecl)。因此不能将被调用者的地址赋值给指针p，尽管两者有相同的返回值和参数列</strong></p>
<h3 id="4-1-3-应用举例"><a href="#4-1-3-应用举例" class="headerlink" title="4.1.3.应用举例"></a>4.1.3.应用举例</h3><p>　　C语言的标准库函数中很多地方就采用了回调函数来让用户定制处理过程。如常用的快速排序函数、二分搜索函数等。</p>
<p>　　快速排序函数原型：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">qsort</span><span class="params">(<span class="type">void</span> *base, <span class="type">size_t</span> nelem, <span class="type">size_t</span> width, <span class="type">int</span> (_USERENTRY *fcmp)(<span class="type">const</span> <span class="type">void</span> *, <span class="type">const</span> <span class="type">void</span> *))</span></span>;</span><br></pre></td></tr></table></figure>

<p>　　二分搜索函数原型：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> *<span class="title">bsearch</span><span class="params">(<span class="type">const</span> <span class="type">void</span> *key, <span class="type">const</span> <span class="type">void</span> *base, <span class="type">size_t</span> nelem, ``<span class="type">size_t</span> width, <span class="type">int</span> (_USERENTRY *fcmp)(<span class="type">const</span> <span class="type">void</span> *, <span class="type">const</span> <span class="type">void</span> *))</span></span>;</span><br></pre></td></tr></table></figure>

<p>　　其中<strong>fcmp就是一个回调函数的变量</strong>。</p>
<p>　　下面给出一个具体的例子：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span> </span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span> </span></span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">sort_function</span><span class="params">( <span class="type">const</span> <span class="type">void</span> *a, <span class="type">const</span> <span class="type">void</span> *b)</span></span>; </span><br><span class="line"><span class="type">int</span> list[<span class="number">5</span>] = &#123; <span class="number">54</span>, <span class="number">21</span>, <span class="number">11</span>, <span class="number">67</span>, <span class="number">22</span> &#125;; </span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">void</span>)</span> </span></span><br><span class="line"><span class="function"></span>&#123; </span><br><span class="line">    <span class="type">int</span> x; </span><br><span class="line"></span><br><span class="line">    <span class="built_in">qsort</span>((<span class="type">void</span> *)list, <span class="number">5</span>, <span class="built_in">sizeof</span>(list[<span class="number">0</span>]), sort_function); </span><br><span class="line">    <span class="keyword">for</span> (x = <span class="number">0</span>; x &lt; <span class="number">5</span>; x++) </span><br><span class="line">    	<span class="built_in">printf</span>(<span class="string">&quot;%in&quot;</span>, list[x]); </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>; </span><br><span class="line">&#125; </span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">sort_function</span><span class="params">( <span class="type">const</span> <span class="type">void</span> *a, <span class="type">const</span> <span class="type">void</span> *b)</span> </span></span><br><span class="line"><span class="function"></span>&#123; </span><br><span class="line">	<span class="keyword">return</span> *(<span class="type">int</span>*)a-*(<span class="type">int</span>*)b; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h1 id="5-Qt调用回调函数"><a href="#5-Qt调用回调函数" class="headerlink" title="5.Qt调用回调函数"></a>5.Qt调用回调函数</h1><h2 id="5-1-dll回调主程序方法"><a href="#5-1-dll回调主程序方法" class="headerlink" title="5.1.dll回调主程序方法"></a>5.1.dll回调主程序方法</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifndef</span> BARCODEASSISTANT_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BARCODEASSISTANT_H</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QtCore/qglobal.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> defined(BARCODEASSISTANT_LIBRARY)<span class="comment">// 定义导出库的名字后将库导出</span></span></span><br><span class="line">    <span class="meta">#<span class="keyword">define</span> BARCODEASSISTANT_EXPORT Q_DECL_EXPORT<span class="comment">// 原始的dll头文件__declspec(dllexport)</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line">    <span class="meta">#<span class="keyword">define</span> BARCODEASSISTANT_EXPORT Q_DECL_IMPORT<span class="comment">// 被使用的头文件作为导入__declspec(dllimport)</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"><span class="comment">// 定义不同平台的回调</span></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)</span></span><br><span class="line">    <span class="meta">#<span class="keyword">ifndef</span> OS_CALLBACK</span></span><br><span class="line">        <span class="meta">#<span class="keyword">define</span> OS_CALLBACK __stdcall<span class="comment">//</span></span></span><br><span class="line">    <span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line">    <span class="meta">#<span class="keyword">ifndef</span> OS_CALLBACK</span></span><br><span class="line">        <span class="meta">#<span class="keyword">ifdef</span> _MIPS_ARCH</span></span><br><span class="line">            <span class="meta">#<span class="keyword">define</span> OS_CALLBACK<span class="comment">//</span></span></span><br><span class="line">        <span class="meta">#<span class="keyword">else</span></span></span><br><span class="line">            <span class="meta">#<span class="keyword">define</span> OS_CALLBACK __attribute__((stdcall))<span class="comment">//</span></span></span><br><span class="line">        <span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">    <span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QString&gt;</span></span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">QDialog</span>;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">QWidget</span>;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">BarCodeWidget</span>;</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">BarcodeInfo</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> BarcodeAssistant</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">enum</span> <span class="title class_">DialogType</span></span><br><span class="line">    &#123;</span><br><span class="line">        BarcodeDlg,</span><br><span class="line">        HistoryDlg</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 以下为对应主程序方法的函数指针</span></span><br><span class="line">    <span class="comment">// 参数为要被添加到阅读器的图片路径</span></span><br><span class="line">    <span class="function"><span class="keyword">typedef</span> <span class="title">void</span> <span class="params">(OS_CALLBACK *AddBarcodeImg)</span><span class="params">(QString)</span></span>;</span><br><span class="line">    <span class="comment">// arg1返回dll生成的图片路径，arg2返回用户选择的路径，二者后缀不一致时进行图片-&gt;OFD/PDF转换</span></span><br><span class="line">    <span class="function"><span class="keyword">typedef</span> <span class="title">void</span> <span class="params">(OS_CALLBACK *SaveBarcodeFile)</span><span class="params">(QString, QString)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> BARCODEASSISTANT_EXPORT <span class="title">CallbackAdd</span><span class="params">(AddBarcodeImg addFunPtr)</span></span>;</span><br><span class="line">    <span class="function"><span class="type">void</span> BARCODEASSISTANT_EXPORT <span class="title">CallbackSave</span><span class="params">(SaveBarcodeFile saveFunPtr)</span></span>;</span><br><span class="line">	<span class="comment">// 以下为导出给主程序使用的初始化动态库和销毁的方法</span></span><br><span class="line">    <span class="function">QDialog BARCODEASSISTANT_EXPORT *<span class="title">Init</span><span class="params">(DialogType type, QWidget *dlgParent)</span></span>;</span><br><span class="line">    <span class="function"><span class="type">void</span> BARCODEASSISTANT_EXPORT <span class="title">Destroy</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span> <span class="comment">// BARCODEASSISTANT_H</span></span></span><br></pre></td></tr></table></figure>

<h2 id="5-2-主程序传入被回调函数地址"><a href="#5-2-主程序传入被回调函数地址" class="headerlink" title="5.2.主程序传入被回调函数地址"></a>5.2.主程序传入被回调函数地址</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">AddBarCode2Page</span><span class="params">(QString)</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">SaveBarCodeImg</span><span class="params">(QString, QString)</span></span>;</span><br><span class="line"></span><br><span class="line">BarcodeAssistant::<span class="built_in">CallbackAdd</span>((BarcodeAssistant::AddBarcodeImg)AddBarCode2Page);</span><br><span class="line">BarcodeAssistant::<span class="built_in">CallbackSave</span>((BarcodeAssistant::SaveBarcodeFile)SaveBarCodeImg);</span><br></pre></td></tr></table></figure>

<h2 id="5-3-在合适时机初始化和销毁"><a href="#5-3-在合适时机初始化和销毁" class="headerlink" title="5.3.在合适时机初始化和销毁"></a>5.3.在合适时机初始化和销毁</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">MainWindow::~<span class="built_in">MainWindow</span>()</span><br><span class="line">&#123;</span><br><span class="line">    BarcodeAssistant::<span class="built_in">Destroy</span>();</span><br><span class="line">    <span class="keyword">delete</span> ui;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">MainWindow::on_BtnHistory_clicked</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    QDialog *dlg = BarcodeAssistant::<span class="built_in">Init</span>(BarcodeAssistant::DialogType::HistoryDlg, QApplication::<span class="built_in">activeModalWidget</span>());</span><br><span class="line">    dlg-&gt;<span class="built_in">show</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">MainWindow::on_BtnBarcode_clicked</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    QDialog *dlg = BarcodeAssistant::<span class="built_in">Init</span>(BarcodeAssistant::BarcodeDlg, QApplication::<span class="built_in">activeModalWidget</span>());</span><br><span class="line">    dlg-&gt;<span class="built_in">show</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="6-类成员函数作为回调函数的方法及注意点"><a href="#6-类成员函数作为回调函数的方法及注意点" class="headerlink" title="6.类成员函数作为回调函数的方法及注意点"></a>6.类成员函数作为回调函数的方法及注意点</h1><p>编程中遇到一个错误，提示为error C2597: illegal reference to non-static member</p>
<p>即因为一个类的静态成员函数调用了类的非静态成员变量，而报错。</p>
<p>下面具体介绍一些相关知识点，以防下次再出错。</p>
<p>类成员函数当回调函数的方法<br>参考自：<a target="_blank" rel="noopener" href="https://blog.csdn.net/this_capslock/article/details/17001003">https://blog.csdn.net/this_capslock/article/details/17001003</a></p>
<h2 id="6-1-回调函数为普通的全局函数，但在函数体内执行类的成员函数"><a href="#6-1-回调函数为普通的全局函数，但在函数体内执行类的成员函数" class="headerlink" title="6.1.回调函数为普通的全局函数，但在函数体内执行类的成员函数"></a>6.1.回调函数为普通的全局函数，但在函数体内执行类的成员函数</h2><p>在创建线程调用回调函数时，<strong>传入类对象的指针（比如this指针）作为参数</strong>，并在回调函数中把void<em>强制转换为类的指针（MyClass</em>），就能使用该指针调用类的成员函数。</p>
<p>这样做的原理是把当前对象的指针当作参数先交给一个外部函数，再由外部函数调用类成员函数。以外部函数作为回调函数，但执行的是成员函数的功能，这样相当于在中间作了一层转换。</p>
<p>缺点：回调函数在类外，<strong>影响了封装性</strong>。</p>
<h2 id="6-2-回调函数为类内静态成员函数，在其内部调用类的非静态成员函数"><a href="#6-2-回调函数为类内静态成员函数，在其内部调用类的非静态成员函数" class="headerlink" title="6.2.回调函数为类内静态成员函数，在其内部调用类的非静态成员函数"></a>6.2.回调函数为类内静态成员函数，在其内部调用类的非静态成员函数</h2><p>此时<em><em>需要一个指向类本身的、类的静态成员变量指针（static MyClass</em> CurMy），用来存储当前回调函数调用的对象</em>*，相当于法1中给回调函数传入的指针参数。在回调函数中通过CurMy指针调用类的成员函数。</p>
<p>优点：</p>
<ul>
<li>1、解决了法1的封装性问题，</li>
<li>2、没有占用callback的参数，可以从外界传递参数进来</li>
</ul>
<p><strong>缺点：每个对象启动子线程前一定要注意先让CurMy正确的指向自身，否则将为其它对象开启线程</strong>。</p>
<h2 id="6-3-对成员函数进行强制转换，使其作为回调函数-建议"><a href="#6-3-对成员函数进行强制转换，使其作为回调函数-建议" class="headerlink" title="6.3.对成员函数进行强制转换，使其作为回调函数(建议)"></a>6.3.对成员函数进行强制转换，使其作为回调函数(建议)</h2><p>这个方法是原理是，MyClass::func最终会转化成 void func(MyClass *this)；即在原第一个参数前插入指向对象本身的this指针。可以利用这个特性写一个非静态类成员方法来直接作为线程回调函数。</p>
<figure class="highlight delphi"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">typedef void* <span class="comment">(*FUNC)(void*)</span>;</span><br><span class="line">FUNC callback = (FUNC)&amp;MyClass::func;</span><br></pre></td></tr></table></figure>
<p>对编译器而言，void (MyClass::<em>FUNC1)()和void</em> (<em>FUNC)(void</em>)这两种函数指针虽然看上去很不一样，但他们的最终形式是相同的，因此就可以<strong>把成员函数指针强制转换成普通函数的指针来当作回调函数</strong>。在建立线程时要把当前对象的指针this当作参数传给回调函数(成员函数func)，这样才能知道线程是针对哪个对象建立的。</p>
<p>注意：此方法中FUNC函数的参数一定要是void*，这样才能在编译后把this指针转变为MyClass *this。</p>
<p>优点：法3的封装性比法2更好，因为不涉及多个对象共用一个静态成员的问题，<strong>每个对象可以独立地启动自己的线程而不影响其它对象</strong>。</p>
<h2 id="6-4-为什么回调函数必须为静态函数？"><a href="#6-4-为什么回调函数必须为静态函数？" class="headerlink" title="6.4.为什么回调函数必须为静态函数？"></a>6.4.为什么回调函数必须为静态函数？</h2><p>普通的C++成员函数都隐含了一个“this”指针参数，当在类的非静态成员函数中访问类的非静态成员时，C++编译器通过传递一个指向对象本身的指针给其成员函数，从而能够访问类的数据成员。也就是说，即使你没有写上this指针，编译器在编译的时候自动加上this的，它作为非静态成员函数的隐含形参，对各成员的访问均通过this进行。</p>
<p>正是由于this指针的作用，使得将一个CALLBACK型的成员函数作为回调函数时就会因为隐含的this指针使得函数参数个数不匹配，从而导致回调函数匹配失败。所以为了实现回调，类中的成员函数必须舍弃掉隐藏的this指针参数。因此，类中的回调函数必须为静态函数，加上static关键字。</p>
<p>类的静态成员函数如何访问非静态成员？<br>静态成员不属于某个具体的对象，而是被所有对象所共享。即静态成员属于整个类，不属于具体某个对象；非静态成员属于具体某个对象。因而静态成员函数只能访问类的静态成员，不能访问类中非静态成员。</p>
<p>那么，如何让静态函数访问类的非静态成员？</p>
<p>方法是：对于静态成员函数，我们显示的为其传递一个对象的首地址（该类的指针）。一般在这个静态成员函数的形参列表中加入一个  void*  类型的参数，来保存对象的首地址。并在该函数内部对该参数进行类型转换，通过类型转换后的参数来调用非静态成员。</p>
<p>或者用一个类的全局指针数组，保存每一个创建出来的类的this指针，用全局指针去调用。</p>
<hr>

        
            <div class="donate-container">
    <div class="donate-button">
        <button id="donate-button">赞赏</button>
    </div>
    <div class="donate-img-container hide" id="donate-img-container">
        <img id="donate-img" src="" data-src="/myblog/img/donate.jpg">
        <p> 感谢鼓励 </p>
    </div>
</div>
        
        <br />
        <div id="comment-container">
        </div>
        <div id="disqus_thread"></div>
        <div id="lv-container"></div>
        <div class="giscus"></div>
    </div>
</div>

    </div>
</div>


<footer class="footer">
    <ul class="list-inline text-center">
        
        

        

        

        
        <li>
            <a target="_blank"  href="https://github.com/samuel-24276">
                            <span class="fa-stack fa-lg">
                                <i class="iconfont icon-github"></i>
                            </span>
            </a>
        </li>
        

        

    </ul>
    
    <p>
        <span>/</span>
        
        <span><a target="_blank" rel="noopener" href="https://github.com/samuel-24276">samuel-24276&#39;s Page</a></span>
        <span>/</span>
        
        <span><a target="_blank" rel="noopener" href="https://beian.miit.gov.cn/">北京-未备案</a></span>
        <span>/</span>
        
        <span><a href="#">返回顶部</a></span>
        <span>/</span>
        
    </p>
    
    <p>
        <span id="busuanzi_container_site_pv">
            <span id="busuanzi_value_site_pv"></span>PV
        </span>
        <span id="busuanzi_container_site_uv">
            <span id="busuanzi_value_site_uv"></span>UV
        </span>
        Created By <a target="_blank" rel="noopener" href="https://hexo.io/">Hexo</a>  Theme <a target="_blank" rel="noopener" href="https://github.com/aircloud/hexo-theme-aircloud">AirCloud</a></p>
</footer>




</body>

<script>
    // We expose some of the variables needed by the front end
    window.hexo_search_path = "search.json"
    window.hexo_root = "/myblog/"
    window.isPost = true
</script>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>

<script src="/myblog/js/index.js"></script>

<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>






    <script type="text/javascript">
       (function() {
           if (typeof LivereTower === 'function') { return; }

           var j, d = document.getElementById('lv-container');

           d.setAttribute('data-id','city');
           d.setAttribute('data-uid' , 'MTAyMC80OTA5OC8yNTU5Mw==');

           j = document.createElement('script');
           j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
           j.async = true;

           d.appendChild(j);
       })();
    </script>
    <noscript>为正常使用来必力评论功能请激活JavaScript</noscript>
    </div>

</html>
